package JET.physics.engine;

import JET.Centers.Scripts.ScriptsCenter;
import JET.geom.Vec2d;
import java.awt.geom.Rectangle2D;
import JET.Constants;
import JET.Frames.DataModels.DataModelsFactory;
import JET.Scene;
import JET.physics.*;
import JET.physics.bodies.*;
import JET.physics.steering.Torque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;

/**
 *
 * @author 057Ry
 */
public final class JETContext implements Constants
{
    public final ArrayList<MassMBCollision> m2dmbColList = new ArrayList<MassMBCollision>();
    public final ArrayList<SBCollision> sbColInfoList = new ArrayList<SBCollision>(200);
    public final ArrayList<SBCollision> rbColInfoList = new ArrayList<SBCollision>(200);
    
    public final ArrayList<Vec2d> hpList = new ArrayList<Vec2d>();
    public final ArrayList<Vec2d> vAList = new ArrayList<Vec2d>();
    public final ArrayList<Vec2d> vBList = new ArrayList<Vec2d>();
    public final ArrayList<Vec2d> vABList = new ArrayList<Vec2d>();
    public int  sbColInfoCount = 0;
    public int  rbColInfoCount = 0;

    public final ArrayList<SpringBody> springBodyList = new ArrayList<SpringBody>();
    public final Collection<SpringBody> springBodyListSync = Collections.synchronizedList(springBodyList);
    public final ArrayList<RigidBody> rigidBodyList = new ArrayList<RigidBody>();
    public final Collection<RigidBody> rigidBodyListSync = Collections.synchronizedList(rigidBodyList);
    public final ArrayList<Spring> springList  = new ArrayList<Spring>();
    public final Collection<Spring> springListSync = Collections.synchronizedList(springList);
    public final ArrayList<RSpring> rSpringList = new ArrayList<RSpring>();
    public final Collection<RSpring> rSpringListSync = Collections.synchronizedList(rSpringList);

    public final ArrayList<Torque> torqueList = new ArrayList<Torque>();
    public final Collection<Torque> torqueListSync = Collections.synchronizedList(torqueList);
    
    Rectangle2D mainBound;
    public int mainBoundMaterial = 0;
    
    double dTime = ENGINE_START_DTIME;      // dlugosc kroku czasowego
    double time = 0.;
    public double damping = 0.9983;          // global friction
    public double gravity = 1.0;
    private boolean applyDamping = false;
    private boolean applyGravity = false;
    public boolean findCollisions = true;
    public boolean resolveCollisions = true;
    
    // Materials
    public MaterialPair[][] materials;
    public String materialNames[];
    public int materialsCount;

    public Scene scene;

    public JETContext() {
        initDefaultMaterials(START_MATERIALS_NUMBER);
        initSBCollisions();
    }

    public void appendJETContext(JETContext c) {
        Collection<SpringBody> sbCol = c.getSpringBodyListSync();
        synchronized(sbCol) {
            for(SpringBody sb : sbCol)
                this.addSpringBody(sb);
        }

        Collection<RigidBody> rbCol = c.getRigidBodyListSync();
        synchronized(rbCol) {
            for(RigidBody rb : rbCol)
                this.addRigidBody(rb);
        }

        Collection<Spring> springCol = c.getSpringListSync();
        synchronized(springCol) {
            for(Spring s : springCol)
                this.addSpring(s);
        }

        Collection<RSpring> rSpringCol = c.getRSpringListSync();
        synchronized(rSpringCol) {
            for(RSpring s : rSpringCol)
                this.addRSpring(s);
        }

        Collection<Torque> torqueCol = c.getTorqueListSync();
        synchronized(torqueCol) {
            for(Torque t : torqueCol)
                this.addTorque(t);
        }
    }

    public void setValuesAndConstantsByJETContext(JETContext c) {
        if (mainBound==null) mainBound = new Rectangle2D.Double();
        mainBound.setRect(c.mainBound);
        mainBoundMaterial = c.mainBoundMaterial;

        dTime = c.dTime;

        time = c.time;
        damping = c.damping;
        gravity = c.gravity;
        applyDamping = c.isApplyDamping();
        applyGravity = c.isApplyGravity();
        findCollisions = c.isFindCollisions();
        resolveCollisions = c.isResolveCollisions();
    }

    public void setMaterialsByJETContext(JETContext context) {
        materialsCount = context.materialsCount;
        materials = new MaterialPair[materialsCount][materialsCount];

        for(int r=0;r<materialsCount; r++)
            for(int c=0;c<materialsCount; c++)
                materials[r][c] = new MaterialPair(context.materials[r][c].friction,
                                                   context.materials[r][c].elasticity,
                                                   context.materials[r][c].collide);

        materialNames = new String[materialsCount];
        for(int i=0; i<materialsCount; i++)
            materialNames[i] = context.materialNames[i];
    }

    public void initSBCollisions() {
        for(int i=0; i<200; i++)
            sbColInfoList.add(new SBCollision());
        for(int i=0; i<200; i++)
            rbColInfoList.add(new SBCollision());
    }

    public void setScene(Scene scene) {
        this.scene = scene;
    }

    public void initDefaultMaterials(int count)
    {
        materialsCount = count;
        materials = new MaterialPair[materialsCount][materialsCount];
        for(int r=0;r<materialsCount; r++)
            for(int c=0;c<materialsCount; c++)
                materials[r][c] = new MaterialPair();

        materialNames = new String[materialsCount];
        for(int i=0; i<materialsCount; i++)
            materialNames[i] = "Material_"+i;
    }

    public void setMainBoundMaterial(int mainBoundMaterial) {this.mainBoundMaterial = mainBoundMaterial;}
    public int getMainBoundMaterial() {return mainBoundMaterial;}
    public void setDamping(double d) {this.damping = d;}
    public double getDamping() {return damping;}
    public void setApplyDamping(boolean applyGlobalFriction) { this.applyDamping = applyGlobalFriction; }
    public boolean isApplyDamping() { return applyDamping; }
    public void setApplyGravity(boolean ag) { applyGravity = ag; }
    public boolean isApplyGravity() {return applyGravity; }
    public void setResolveCollisions(boolean resolveCollisions) { this.resolveCollisions = resolveCollisions; }
    public boolean isResolveCollisions() { return resolveCollisions; }
    public void setFindCollisions(boolean findCollisions) { this.findCollisions = findCollisions; }
    public boolean isFindCollisions() { return findCollisions; }
    public void setDTime(double dTime) { this.dTime = dTime; }
    public double getDTime() { return dTime; }
    public void setTime(double t) { time = t; }
    public double getTime() { return time; }
    public void setMainBound(double width, double height) {mainBound = new Rectangle2D.Double(0.,0., width, height);}
    public Rectangle2D getMainBound() {return mainBound;}

    public void addSpringBody(SpringBody sb) {
        springBodyList.add( sb );
    }
    public void removeSpringBody(int index) {
        SpringBody sb = springBodyList.remove(index);
    }
    public void removeSpringBody(SpringBody sb) {
        springBodyList.remove(sb);
    }
    public ArrayList<SpringBody> getSpringBodyList() {
        return springBodyList;
    }

    public Collection<SpringBody> getSpringBodyListSync() {
        return springBodyListSync;
        //return Collections.synchronizedList(springBodyList);
    }

    public void addRigidBody(RigidBody rb) {
        rigidBodyList.add( rb );
    }
    public void removeRigidBody(int index) {
        rigidBodyList.remove( index );
    }
    public void removeRigidBody(RigidBody rb) {
        rigidBodyList.remove( rb );
    }

    public ArrayList<RigidBody> getRigidBodyList() {
        return rigidBodyList;
    }

    public Collection<RigidBody> getRigidBodyListSync() {
        return rigidBodyListSync;
        //return Collections.synchronizedList(RigidBodyList);
    }
    
    public synchronized ArrayList<Torque> getTorqueList() {
        return torqueList;
    }

    public Collection<Torque> getTorqueListSync() {
        return torqueListSync;
        //return Collections.synchronizedList(torqueList);
    }

    public void addTorque(Torque t) {
        getTorqueList().add(t);
        DataModelsFactory.getSceneTreeModel().fireSceneTorquesChanged();
    }

    public void removeTorque(Torque t) {
        getTorqueList().remove(t);
        DataModelsFactory.getSceneTreeModel().fireSceneTorquesChanged();
    }

    public ArrayList<Torque> getTorqueListThatUsesMass(Mass mass) {
        ArrayList<Torque> list = new ArrayList<Torque>();

        for(Torque t : getTorqueList())
            if (t.getMass()!=null && t.getMass().equals(mass))
                list.add(t);

        return list;

    }
    public void addSpring(Spring spring) {
        synchronized(springListSync) {
            if (!springListSync.contains(spring))
                springListSync.add(spring);
        }
        //this.springList.add(spring);
    }
    public void removeSpring(Spring spring) {
        synchronized(springListSync) {
            springListSync.remove(spring);
        }
    }
    
    public synchronized ArrayList<Spring> getSpringList() {
        return this.springList;
    }
    public synchronized Collection<Spring> getSpringListSync() {
        return springListSync;
        //return Collections.synchronizedList(springList);
    }
    
    public Spring getSpringAtPos(Vec2d vec, double catchAreaScale) {
        for( Spring spring : this.springList)
            if( spring.getCatchArea(catchAreaScale).contains(vec) )
                return spring;
        return null;
    }

    public void addRSpring(RSpring rSpring) {
        synchronized(rSpringListSync) {
            if (!rSpringListSync.contains(rSpring))
                this.rSpringList.add(rSpring);
        }
    }

    public void removeRSpring(RSpring rSpring) {
        synchronized(rSpringListSync) {
            this.rSpringListSync.remove(rSpring);
        }
    }
    public synchronized ArrayList<RSpring> getRSpringList() {
        return this.rSpringList;
    }
    public synchronized Collection<RSpring> getRSpringListSync() {
        return rSpringListSync;
        //return Collections.synchronizedList(rSpringList);
    }
    public RSpring getRSpringAtPos(Vec2d vec, double catchAreaScale) {
        for(RSpring rSpring : this.rSpringList)
            if( rSpring.getCatchArea(1, catchAreaScale).contains(vec) ||
                rSpring.getCatchArea(2, catchAreaScale).contains(vec) ||
                rSpring.getCatchArea(3, catchAreaScale).contains(vec)  )
                return rSpring;
        return null;
    }

    ////////////////////////////////////////////////////////////////////////////
    //               J A V A   S C R I P T   H E L P E R S                    //
    ////////////////////////////////////////////////////////////////////////////

    public SpringBody getSpringBodyByName(String name) {
        synchronized(springBodyListSync) {
            for(SpringBody sb : springBodyListSync)
                if (sb.getName().equals(name))
                    return sb;
        }

        return null;
    }

    public Mass getMassByName(String name) {
        synchronized(springBodyListSync) {
            for(SpringBody sb : springBodyListSync) {
                Collection<Mass> massCol = sb.getMassListSync();
                for(Mass m : massCol)
                    if (m.getName().equals(name))
                        return m;
            }
        }

        return null;
    }

    public Spring getSpringByName(String name) {
        synchronized(springListSync) {
            for(Spring s : springListSync)
                if (s.getName().equals(name))
                    return s;
        }

        synchronized(springBodyListSync) {
            for(SpringBody sb : springBodyListSync)
                for(Spring s : sb.getSpringList())
                    if (s.getName().equals(name))
                        return s;
        }

        return null;
    }

    public RSpring getRSpringByName(String name) {
        synchronized(rSpringListSync) {
            for(RSpring rs : rSpringListSync)
                if (rs.getName().equals(name))
                    return rs;
        }

        synchronized(springBodyListSync) {
            for(SpringBody sb : springBodyListSync)
                for(RSpring rs : sb.getRSpringList())
                    if (rs.getName().equals(name))
                        return rs;
        }

        return null;
    }

    public Torque getTorqueByName(String name) {
        synchronized(torqueListSync) {
            for(Torque t : torqueListSync)
                if (t.getName().equals(name))
                    return t;
        }
        
        return null;
    }
    
    ////////////////////////////////////////////////////////////////////////////
    //                   S C E N E   T R E E   N O D E S                      //
    ////////////////////////////////////////////////////////////////////////////

    public final SpringBodiesTreeNode springBodiesTreeNode = new SpringBodiesTreeNode(springBodyList);
    public final SpringsTreeNode springsTreeNode = new SpringsTreeNode(springList);
    public final RSpringsTreeNode rSpringsTreeNode = new RSpringsTreeNode(rSpringList);
    public final TorquesTreeNode torquesTreeNode = new TorquesTreeNode(torqueList);
}
